﻿using Sustainalytics.Claims;
using Sustainalytics.Entities;
using Sustainalytics.News.BusinessLogic;
using Sustainalytics.News.Service.Utility;
using Sustainalytics.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Cors;
using WebApi.OutputCache.V2;
using Ent = Sustainalytics.Entities;

namespace Sustainalytics.News.Service
{
    /// <summary>
    /// News controller.
    /// </summary>
    //[AutoInvalidateCacheOutput]
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [RoutePrefix("api/v1/news")]
    public class NewsController : ApiController, INewsController
    {
        /// <summary>
        /// News manager.
        /// </summary>
        private readonly INewsManagement _newsManager;
        private readonly IClaimsChecker _claimsChecker;

        /// <summary>
        /// Ctor.
        /// </summary>
        public NewsController(
                INewsManagement newsManager,
                IClaimsChecker claimsChecker
            )
        {
            _newsManager = newsManager;
            _claimsChecker = claimsChecker;
        }

        /// <summary>
        /// Get news filtered.
        /// </summary>
        /// <param name="pageIndex"> Page index. </param>
        /// <param name="pageSize"> Page size. </param>
        /// <returns> Paged list of news. </returns>
       // [CacheOutput(ServerTimeSpan = 100, ClientTimeSpan = 0, NoCache = true, MustRevalidate = true, Private = true)]
        [Route("")]
        public HttpResponseMessage Get(
            [FromUri]ListOptions listOptions, 
            Guid? accountId, 
            string term = null, 
            NewsCategory? category = null, 
            bool? isPublished = true, 
            NewsAvailability availableFilter = NewsAvailability.Client, 
            bool truncateBody = false, 
            bool fromAdmin = false)
        {
            try
            {
                if (!fromAdmin)
                {
                    if (!_claimsChecker.ValidateClaim(this))
                        throw new DashboardClaimNullException();
                }
                else
                {
                    if (!_claimsChecker.ValidateAdminClaim(this))
                        throw new AdminClaimNullException();
                }

                listOptions = listOptions ?? ListOptions.Default;

                var filter = new NewsFilter
                {
                    Term = term,
                    Category = category ?? null,
                    IsPublished = isPublished ?? true,
                    AvailableFilter = availableFilter
                };

                if (listOptions.GetCount)
                {
                    int newsCnt = _newsManager.GetNewsCount(filter);
                    return Request.CreateResponse(HttpStatusCode.OK, newsCnt);
                }
                else
                {
                    List<Ent.News> newsLst = _newsManager.GetNews(
                        filter: filter,
                        accountId: accountId,
                        skip: listOptions.Skip,
                        take: listOptions.Take,
                        orderBy: listOptions.OrderBy,
                        orderByAsc: listOptions.OrderAscending
                        );

                    if (listOptions.IncludeAllFields)
                    {
                        List<NewsDto> newsDtoLst = newsLst.Select(n => new NewsDto().LoadFrom(n, truncateBody)).ToList();
                        return this.Request.CreateResponse(HttpStatusCode.OK, newsDtoLst);
                    }
                    else
                    {
                        List<NewsBaseDto> newsDtoLst = newsLst.Select(n => new NewsDto().LoadLess(n)).ToList();
                        return this.Request.CreateResponse(HttpStatusCode.OK, newsDtoLst);
                    }
                }
            }
            catch (DashboardClaimNullException)
            {
                return this.Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = ErrorCodes.NewsErrorCodes.News_DashboardClaimNull });
            }
            catch (AdminClaimNullException)
            {
                return this.Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = ErrorCodes.NewsErrorCodes.News_AdminClaimNull });
            }
            catch (NewsFilterNotValidException ex)
            {
                return this.Request.CreateResponse(HttpStatusCode.BadRequest, ex.GetHttpResponseMessageDto());
            }
        }

        /// <summary>
        /// Get news by ID.
        /// </summary>
        /// <param name="newsId"> News ID. </param>
        /// <returns> News. </returns>
       // [CacheOutput(ServerTimeSpan = 100, ClientTimeSpan = 0, NoCache = true, MustRevalidate = true, Private = true)]
        [Route("{newsId}")]
        public HttpResponseMessage Get(Guid newsId, Guid? accountId, bool fromAdmin = false)
        {
            try
            {
                if (!fromAdmin)
                {
                    if (!_claimsChecker.ValidateClaim(this))
                        throw new DashboardClaimNullException();
                }
                else
                {
                    if (!_claimsChecker.ValidateAdminClaim(this))
                        throw new AdminClaimNullException();
                }

                Ent.News news = _newsManager.GetNews(newsId, accountId);

                if (news == null)
                    return null;

                NewsDto newsDto = new NewsDto();
                newsDto.LoadFrom(news);

                return Request.CreateResponse(HttpStatusCode.OK, newsDto);
            }
            catch (DashboardClaimNullException)
            {
                return Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = ErrorCodes.NewsErrorCodes.News_DashboardClaimNull });
            }
            catch (AdminClaimNullException)
            {
                return Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = ErrorCodes.NewsErrorCodes.News_AdminClaimNull });
            }
            catch (NewsNotAvailableForAccount)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorCode = ErrorCodes.NewsErrorCodes.News_NotAvailableForAccount });
            }
        }


        /// <summary>
        /// Add news.
        /// </summary>
        /// <param name="news"> News data. </param>
        /// <returns> Added news. </returns>
        [Route("")]
        public HttpResponseMessage Post(NewsDto news)
        {
            try
            {
                if (!_claimsChecker.ValidateAdminClaim(this))
                    throw new AdminClaimNullException();

                if (news == null)
                    throw new NewsNullException();

                Ent.News newsEnt = new Ent.News();
                news.SaveToNew(newsEnt);

                Ent.News newNews = _newsManager.AddNews(newsEnt);

                news.LoadFrom(newNews);

                return Request.CreateResponse(HttpStatusCode.OK, news);
            }
            catch (AdminClaimNullException)
            {
                return Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = ErrorCodes.NewsErrorCodes.News_AdminClaimNull });
            }
            catch (NewsNotValidException ex)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, ex.GetHttpResponseMessageDto());
            }
            catch (NewsNullException ex)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, ex.GetHttpResponseMessageDto());
            }
            catch (PublishNonDraftNewsException ex)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, ex.GetHttpResponseMessageDto());
            }
        }

        /// <summary>
        /// Update news.
        /// </summary>
        /// <param name="news"> News data. </param>
        /// <returns> Updated news. </returns>
        [Route("")]
        public HttpResponseMessage Put(NewsDto news)
        {
            try
            {
                if (!_claimsChecker.ValidateAdminClaim(this))
                    throw new AdminClaimNullException();

                if (news == null)
                    throw new NewsNullException();

                Ent.News newsEnt = new Ent.News();
                news.SaveToExisting(newsEnt);

                Ent.News newNews = _newsManager.UpdateNews(newsEnt);

                news.LoadFrom(newNews);

                return Request.CreateResponse(HttpStatusCode.OK, news);
            }
            catch (AdminClaimNullException)
            {
                return Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = ErrorCodes.NewsErrorCodes.News_AdminClaimNull });
            }
            catch (NewsNotValidException ex)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, ex.GetHttpResponseMessageDto());
            }
            catch (NewsNullException ex)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, ex.GetHttpResponseMessageDto());
            }
            catch (PublishNonDraftNewsException ex)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, ex.GetHttpResponseMessageDto());
            }
            catch (NewsDoesNotExistException ex)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, ex.GetHttpResponseMessageDto());
            }
        }

        /// <summary>
        /// Delete news items.
        /// </summary>
        [Route("{newsId}")]
        public HttpResponseMessage Delete(Guid newsId)
        {
            try
            {
                if (!_claimsChecker.ValidateAdminClaim(this))
                    throw new AdminClaimNullException();

                _newsManager.DeleteNews(newsId);

                return Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (AdminClaimNullException)
            {
                return Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = ErrorCodes.NewsErrorCodes.News_AdminClaimNull });
            }
            catch (NewsDoesNotExistException ex)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, ex.GetHttpResponseMessageDto());
            }
            catch (DeleteNonDraftNewsException ex)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, ex.GetHttpResponseMessageDto());
            }
        }

        /// <summary>
        /// Accepts a batch of operations to perform on the resource.
        /// </summary>
        /// <param name="patchData"> Batch operations. </param>
        [Route("")]

        [InvalidateCacheOutput("Get")]
        public HttpResponseMessage Patch([FromBody]NewsPatch patchData)
        {
            try
            {
                if (!_claimsChecker.ValidateAdminClaim(this))
                    throw new AdminClaimNullException();

                _newsManager.PatchNews(patchData);

                return Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (AdminClaimNullException)
            {
                return Request.CreateResponse(HttpStatusCode.Forbidden, new { ErrorCode = ErrorCodes.NewsErrorCodes.News_AdminClaimNull });
            }
            catch (NewsPatchDataNullException ex)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, ex.GetHttpResponseMessageDto());
            }
            catch (PublishNonDraftNewsException ex)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, ex.GetHttpResponseMessageDto());
            }
            catch (DeleteNonDraftNewsException ex)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, ex.GetHttpResponseMessageDto());
            }
            catch (NewsIdsNullException ex)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, ex.GetHttpResponseMessageDto());
            }
            finally
            {
                var cache = Configuration.CacheOutputConfiguration().GetCacheOutputProvider(Request);
                cache.RemoveStartsWith(Configuration.CacheOutputConfiguration().MakeBaseCachekey("News", "Get"));
            }
        }
    }
}
